Skip to content

[0.7.1] Release - #36

Closed
AGiorgetti wants to merge 12 commits into
mainfrom
release/0.7.1
Closed

[0.7.1] Release#36
AGiorgetti wants to merge 12 commits into
mainfrom
release/0.7.1

Conversation

@AGiorgetti

Copy link
Copy Markdown
Contributor

This pull request introduces a patch release (0.7.1) focused on bug fixes and improvements to service registration and resolution validation in the dependency injection library. The most significant changes address lifetime check methods, resolution context tracking, and registration behaviors for empty dependency arrays. Comprehensive tests have been added to verify these fixes and behaviors.

Bug Fixes and Validation Improvements

  • The IServiceCollection lifetime check methods (IsTransientServiceRegistered, IsScopedServiceRegistered, IsSingletonServiceRegistered, and their keyed variants) now return false instead of throwing InvalidOperationException when the service is not registered.
  • Fixed stack corruption in ResolutionContext when a factory delegate throws an exception by wrapping Push/Pop calls in try/finally for all registration paths, ensuring stack cleanliness after exceptions. [1] [2]
  • Corrected the GetServiceDescriptors method to use unidirectional IsAssignableFrom checks, preventing incorrect lifetime checks and unrelated base-type registrations.

Registration Behavior

  • Fixed missing return statements in TryAdd* overloads with empty DependsOn arrays, preventing unnecessary reflection and potential double-registration.
  • Added tests to confirm that TryAddSingleton, TryAddScoped, TryAddTransient, and TryAddKeyedSingleton correctly register services when provided an empty dependency array. [1] [2]

Test Coverage

  • Added extensive tests for lifetime check methods to ensure correct behavior for both registered and unregistered services, including keyed variants.
  • Introduced tests for async disposable transient services and consumers, validating correct exception handling, memory leak prevention, and proper disposal in scoped and root contexts. [1] [2]
  • Added tests to ensure resolution context stack remains clean after exceptions in factory and constructor paths, for both keyed and non-keyed registrations.

These changes collectively improve reliability, correctness, and developer experience when working with service registrations and resolution in the library.

AGiorgetti and others added 11 commits February 26, 2026 15:51
… DependsOn

- Added return statements after inner TryAdd* calls when dependsOn.Length == 0
- Prevents unnecessary reflection calls to GetConstructorAndParameters
- Avoids potential double-registration attempts
- Fixes 14 TryAdd* method overloads:
  - TryAddSingleton (3 overloads)
  - TryAddScoped (3 overloads)
  - TryAddTransient (2 overloads)
  - TryAddKeyedSingleton (2 overloads)
  - TryAddKeyedScoped (2 overloads)
  - TryAddKeyedTransient (2 overloads)
- Changed GetServiceDescriptors to use unidirectional type matching:
  serviceType == serviceDescriptor.ServiceType || serviceType.IsAssignableFrom(serviceDescriptor.ServiceType)

- This prevents unrelated base-type registrations (like object) from incorrectly
  matching specific type queries, which was causing .Last() to return wrong descriptors

- Fixes incorrect lifetime checks in IsTransientServiceRegistered, IsSingletonServiceRegistered,
  IsScopedServiceRegistered and their keyed variants

- Added 6 comprehensive tests to verify the fix and prevent regression
…19 tests

Co-authored-by: AGiorgetti <246067+AGiorgetti@users.noreply.github.com>
…able

Co-authored-by: AGiorgetti <246067+AGiorgetti@users.noreply.github.com>
Co-authored-by: AGiorgetti <246067+AGiorgetti@users.noreply.github.com>
…g to prevent stack corruption on factory exception

Co-authored-by: AGiorgetti <246067+AGiorgetti@users.noreply.github.com>
Co-authored-by: AGiorgetti <246067+AGiorgetti@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
…stead of throwing

Co-authored-by: AGiorgetti <246067+AGiorgetti@users.noreply.github.com>
… equality

Co-authored-by: AGiorgetti <246067+AGiorgetti@users.noreply.github.com>
Copilot AI review requested due to automatic review settings February 27, 2026 10:47
@AGiorgetti AGiorgetti self-assigned this Feb 27, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request introduces version 0.7.1, a patch release focused on critical bug fixes in service registration validation and resolution context tracking. The changes address three main issues: (1) incorrect bidirectional type matching in service descriptor lookups that caused false positives with base types, (2) stack corruption in resolution context tracking when exceptions occur during service resolution, and (3) missing early returns in dependency registration methods that caused unnecessary reflection operations.

Changes:

  • Fixed GetServiceDescriptors to use unidirectional IsAssignableFrom checking, preventing incorrect lifetime validation when base types (like object) are registered with different lifetimes
  • Enhanced exception safety by wrapping all ResolutionContext.CurrentStack Push/Pop operations in try/finally blocks across all four registration paths (non-keyed/keyed × factory/type)
  • Extended transient disposable detection to include IAsyncDisposable in addition to IDisposable, with a new IsDisposableType helper method
  • Fixed missing return statements in TryAdd* methods when DependsOn arrays are empty, avoiding unnecessary reflection and potential double-registration
  • Updated lifetime check methods to return false for unregistered services instead of throwing exceptions
  • Changed keyed service comparisons from == to Equals() for proper value-based equality

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
ServiceCollectionExtensions.Registration.cs Fixed bidirectional IsAssignableFrom to unidirectional, updated lifetime checks to return false for empty descriptors, changed key comparisons to use Equals
ServiceCollectionExtensions.KeyedService.cs Added missing return statements in all TryAdd* overloads when dependsOn array is empty
DetectIncorrectUsageOfTransientDisposables.cs Wrapped Push/Pop in try/finally for exception safety, added IAsyncDisposable support via IsDisposableType helper
ServiceCollectionExtensions.Registration.Tests.cs New test file with comprehensive coverage for lifetime check methods returning false for unregistered services
ServiceProviderExtensions.Registration.Tests.cs Added 6 tests for Issue #19 validating unidirectional IsAssignableFrom fixes cross-type matching bugs
ServiceCollectionExtensions.KeyedService.Tests.cs Added 4 tests verifying TryAdd* methods correctly register services with empty DependsOn arrays
DetectIncorrectUsageOfTransientDisposablesTests.cs Added 8 tests for async disposable detection, memory leak prevention, and stack cleanup after exceptions
Changelog.md Added version 0.7.1 section documenting all bug fixes with issue references

@AGiorgetti AGiorgetti closed this Feb 27, 2026
@AGiorgetti
AGiorgetti deleted the release/0.7.1 branch February 27, 2026 11:01
@AGiorgetti

Copy link
Copy Markdown
Contributor Author

The release branch was closed manually using git flow.

@AGiorgetti
AGiorgetti restored the release/0.7.1 branch February 27, 2026 11:03
@AGiorgetti
AGiorgetti deleted the release/0.7.1 branch February 27, 2026 11:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants